{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 用户冷启动-利用用户注册信息"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 导入包\n",
    "import random\n",
    "import math\n",
    "import numpy as np\n",
    "import time\n",
    "from tqdm import tqdm, trange"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 一. 通用函数定义"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 定义装饰器，监控运行时间\n",
    "def timmer(func):\n",
    "    def wrapper(*args, **kwargs):\n",
    "        start_time = time.time()\n",
    "        res = func(*args, **kwargs)\n",
    "        stop_time = time.time()\n",
    "        print('Func %s, run time: %s' % (func.__name__, stop_time - start_time))\n",
    "        return res\n",
    "    return wrapper"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 1. 数据处理相关\n",
    "1. load data\n",
    "2. split data"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "class Dataset():\n",
    "    \n",
    "    def __init__(self, fp, up):\n",
    "        # fp: data file path\n",
    "        # up: user profile path\n",
    "        self.data, self.profile = self.loadData(fp, up)\n",
    "    \n",
    "    @timmer\n",
    "    def loadData(self, fp, up):\n",
    "        data = []\n",
    "        for l in open(fp):\n",
    "            data.append(tuple(l.strip().split('\\t')[:2]))\n",
    "        profile = {}\n",
    "        for l in open(up):\n",
    "            user, gender, age, country, _ = l.strip().split('\\t')\n",
    "            if age == '': \n",
    "                age = -1\n",
    "            profile[user] = {'gender': gender, 'age': int(age), 'country': country}\n",
    "        # 按照用户进行采样\n",
    "        users = list(profile.keys())\n",
    "        random.shuffle(users)\n",
    "        users = set(users[:5000])\n",
    "        data = [x for x in data if x[0] in users]\n",
    "        profile = {k: profile[k] for k in users}\n",
    "        return data, profile\n",
    "    \n",
    "    @timmer\n",
    "    def splitData(self, M, k, seed=1):\n",
    "        '''\n",
    "        :params: data, 加载的所有(user, item)数据条目\n",
    "        :params: M, 划分的数目，最后需要取M折的平均\n",
    "        :params: k, 本次是第几次划分，k~[0, M)\n",
    "        :params: seed, random的种子数，对于不同的k应设置成一样的\n",
    "        :return: train, test\n",
    "        '''\n",
    "        train, test = [], []\n",
    "        random.seed(seed)\n",
    "        for user, item in self.data:\n",
    "            # 这里与书中的不一致，本人认为取M-1较为合理，因randint是左右都覆盖的\n",
    "            if random.randint(0, M-1) == k:  \n",
    "                test.append((user, item))\n",
    "            else:\n",
    "                train.append((user, item))\n",
    "\n",
    "        # 处理成字典的形式，user->set(items)\n",
    "        def convert_dict(data):\n",
    "            data_dict = {}\n",
    "            for user, item in data:\n",
    "                if user not in data_dict:\n",
    "                    data_dict[user] = set()\n",
    "                data_dict[user].add(item)\n",
    "            data_dict = {k: list(data_dict[k]) for k in data_dict}\n",
    "            return data_dict\n",
    "\n",
    "        return convert_dict(train), convert_dict(test), self.profile"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 2. 评价指标\n",
    "1. Precision\n",
    "2. Recall\n",
    "3. Coverage"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "class Metric():\n",
    "    \n",
    "    def __init__(self, train, test, GetRecommendation):\n",
    "        '''\n",
    "        :params: train, 训练数据\n",
    "        :params: test, 测试数据\n",
    "        :params: GetRecommendation, 为某个用户获取推荐物品的接口函数\n",
    "        '''\n",
    "        self.train = train\n",
    "        self.test = test\n",
    "        self.GetRecommendation = GetRecommendation\n",
    "        self.recs = self.getRec()\n",
    "        \n",
    "    # 为test中的每个用户进行推荐\n",
    "    def getRec(self):\n",
    "        recs = {}\n",
    "        for user in self.test:\n",
    "            rank = self.GetRecommendation(user)\n",
    "            recs[user] = rank\n",
    "        return recs\n",
    "        \n",
    "    # 定义精确率指标计算方式\n",
    "    def precision(self):\n",
    "        all, hit = 0, 0\n",
    "        for user in self.test:\n",
    "            test_items = set(self.test[user])\n",
    "            rank = self.recs[user]\n",
    "            for item, score in rank:\n",
    "                if item in test_items:\n",
    "                    hit += 1\n",
    "            all += len(rank)\n",
    "        return round(hit / all * 100, 2)\n",
    "    \n",
    "    # 定义召回率指标计算方式\n",
    "    def recall(self):\n",
    "        all, hit = 0, 0\n",
    "        for user in self.test:\n",
    "            test_items = set(self.test[user])\n",
    "            rank = self.recs[user]\n",
    "            for item, score in rank:\n",
    "                if item in test_items:\n",
    "                    hit += 1\n",
    "            all += len(test_items)\n",
    "        return round(hit / all * 100, 2)\n",
    "    \n",
    "    # 定义覆盖率指标计算方式\n",
    "    def coverage(self):\n",
    "        all_item, recom_item = set(), set()\n",
    "        for user in self.test:\n",
    "            if user in self.train:\n",
    "                for item in self.train[user]:\n",
    "                    all_item.add(item)\n",
    "            rank = self.recs[user]\n",
    "            for item, score in rank:\n",
    "                recom_item.add(item)\n",
    "        return round(len(recom_item) / len(all_item) * 100, 2)\n",
    "    \n",
    "    def eval(self):\n",
    "        metric = {'Precision': self.precision(),\n",
    "                  'Recall': self.recall(),\n",
    "                  'Coverage': self.coverage()}\n",
    "        print('Metric:', metric)\n",
    "        return metric"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 二. 算法实现\n",
    "1. MostPopular\n",
    "2. GenderMostPopular\n",
    "3. AgeMostPopular\n",
    "4. CountryMostPopular\n",
    "5. DemographicMostPopular"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 1. MostPopular算法\n",
    "def MostPopular(train, profile, N):\n",
    "    '''\n",
    "    :params: train, 训练数据\n",
    "    :params: profile, 用户的注册信息\n",
    "    :params: N, 推荐TopN物品的个数\n",
    "    :return: GetRecommendation, 获取推荐结果的接口\n",
    "    '''\n",
    "    \n",
    "    items = {}\n",
    "    for user in train:\n",
    "        for item in train[user]:\n",
    "            if item not in items:\n",
    "                items[item] = 0\n",
    "            items[item] += 1\n",
    "    items = list(sorted(items.items(), key=lambda x: x[1], reverse=True))\n",
    "        \n",
    "    # 获取接口函数\n",
    "    def GetRecommendation(user):\n",
    "        seen_items = set(train[user]) if user in train else set()\n",
    "        recs = [x for x in items if x[0] not in seen_items][:N]\n",
    "        return recs\n",
    "    \n",
    "    return GetRecommendation"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 2. GenderMostPopular算法\n",
    "def GenderMostPopular(train, profile, N):\n",
    "    '''\n",
    "    :params: train, 训练数据\n",
    "    :params: profile, 用户的注册信息\n",
    "    :params: N, 推荐TopN物品的个数\n",
    "    :return: GetRecommendation, 获取推荐结果的接口\n",
    "    '''\n",
    "    \n",
    "    mitems, fitems = {}, {} # 男、女\n",
    "    for user in train:\n",
    "        if profile[user]['gender'] == 'm':\n",
    "            tmp = mitems\n",
    "        elif profile[user]['gender'] == 'f':\n",
    "            tmp = fitems\n",
    "        for item in train[user]:\n",
    "            if item not in tmp:\n",
    "                tmp[item] = 0\n",
    "            tmp[item] += 1\n",
    "    mitems = list(sorted(mitems.items(), key=lambda x: x[1], reverse=True))\n",
    "    fitems = list(sorted(fitems.items(), key=lambda x: x[1], reverse=True))\n",
    "    \n",
    "    mostPopular = MostPopular(train, profile, N)\n",
    "    \n",
    "    # 获取接口函数\n",
    "    def GetRecommendation(user):\n",
    "        seen_items = set(train[user]) if user in train else set()\n",
    "        if profile[user]['gender'] == 'm':\n",
    "            recs = [x for x in mitems if x[0] not in seen_items][:N]\n",
    "        elif profile[user]['gender'] == 'f':\n",
    "            recs = [x for x in fitems if x[0] not in seen_items][:N]\n",
    "        else: # 没有提供性别信息的，按照MostPopular推荐\n",
    "            recs = mostPopular(user)\n",
    "        return recs\n",
    "    \n",
    "    return GetRecommendation"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 3. AgeMostPopular算法\n",
    "def AgeMostPopular(train, profile, N):\n",
    "    '''\n",
    "    :params: train, 训练数据\n",
    "    :params: profile, 用户的注册信息\n",
    "    :params: N, 推荐TopN物品的个数\n",
    "    :return: GetRecommendation, 获取推荐结果的接口\n",
    "    '''\n",
    "    \n",
    "    # 对年龄进行分段\n",
    "    ages = []\n",
    "    for user in profile:\n",
    "        if profile[user]['age'] >= 0:\n",
    "            ages.append(profile[user]['age'])\n",
    "    maxAge, minAge = max(ages), min(ages)\n",
    "    items = [{} for _ in range(int(maxAge // 10 + 1))]\n",
    "    \n",
    "    # 分年龄段进行统计\n",
    "    for user in train:\n",
    "        if profile[user]['age'] >= 0:\n",
    "            age = profile[user]['age'] // 10\n",
    "            for item in train[user]:\n",
    "                if item not in items[age]:\n",
    "                    items[age][item] = 0\n",
    "                items[age][item] += 1\n",
    "    for i in range(len(items)):\n",
    "        items[i] = list(sorted(items[i].items(), key=lambda x: x[1], reverse=True))\n",
    "    \n",
    "    mostPopular = MostPopular(train, profile, N)\n",
    "    \n",
    "    # 获取接口函数\n",
    "    def GetRecommendation(user):\n",
    "        seen_items = set(train[user]) if user in train else set()\n",
    "        if profile[user]['age'] >= 0:\n",
    "            age = profile[user]['age'] // 10\n",
    "            # 年龄信息异常的，按照全局推荐\n",
    "            if age >= len(items) or len(items[age]) == 0:\n",
    "                recs = mostPopular(user)\n",
    "            else:\n",
    "                recs = [x for x in items[age] if x[0] not in seen_items][:N]\n",
    "        else: # 没有提供年龄信息的，按照全局推荐\n",
    "            recs = mostPopular(user)\n",
    "        return recs\n",
    "    \n",
    "    return GetRecommendation"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 4. CountryMostPopular算法\n",
    "def CountryMostPopular(train, profile, N):\n",
    "    '''\n",
    "    :params: train, 训练数据\n",
    "    :params: profile, 用户的注册信息\n",
    "    :params: N, 推荐TopN物品的个数\n",
    "    :return: GetRecommendation, 获取推荐结果的接口\n",
    "    '''\n",
    "        \n",
    "    # 分城市进行统计\n",
    "    items = {}\n",
    "    for user in train:\n",
    "        country = profile[user]['country']\n",
    "        if country not in items:\n",
    "            items[country] = {}\n",
    "        for item in train[user]:\n",
    "            if item not in items[country]:\n",
    "                items[country][item] = 0\n",
    "            items[country][item] += 1\n",
    "    for country in items:\n",
    "        items[country] = list(sorted(items[country].items(), key=lambda x: x[1], reverse=True))\n",
    "    \n",
    "    mostPopular = MostPopular(train, profile, N)\n",
    "    \n",
    "    # 获取接口函数\n",
    "    def GetRecommendation(user):\n",
    "        seen_items = set(train[user]) if user in train else set()\n",
    "        country = profile[user]['country']\n",
    "        if country in items:\n",
    "            recs = [x for x in items[country] if x[0] not in seen_items][:N]\n",
    "        else: # 没有提供城市信息的，按照全局推荐\n",
    "            recs = mostPopular(user)\n",
    "        return recs\n",
    "    \n",
    "    return GetRecommendation"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 5. DemographicMostPopular算法\n",
    "def DemographicMostPopular(train, profile, N):\n",
    "    '''\n",
    "    :params: train, 训练数据\n",
    "    :params: profile, 用户的注册信息\n",
    "    :params: N, 推荐TopN物品的个数\n",
    "    :return: GetRecommendation, 获取推荐结果的接口\n",
    "    '''\n",
    "\n",
    "    # 建立多重字典，将缺失值当成other，同归为一类进行处理\n",
    "    items = {}\n",
    "    for user in train:\n",
    "        gender = profile[user]['gender']\n",
    "        if gender not in items:\n",
    "            items[gender] = {}\n",
    "        age = profile[user]['age'] // 10\n",
    "        if age not in items[gender]:\n",
    "            items[gender][age] = {}\n",
    "        country = profile[user]['country']\n",
    "        if country not in items[gender][age]:\n",
    "            items[gender][age][country] = {}\n",
    "        for item in train[user]:\n",
    "            if item not in items[gender][age][country]:\n",
    "                items[gender][age][country][item] = 0\n",
    "            items[gender][age][country][item] += 1\n",
    "    for gender in items:\n",
    "        for age in items[gender]:\n",
    "            for country in items[gender][age]:\n",
    "                items[gender][age][country] = list(sorted(items[gender][age][country].items(), \n",
    "                                                          key=lambda x: x[1], reverse=True))\n",
    "                \n",
    "    mostPopular = MostPopular(train, profile, N)\n",
    "    \n",
    "    # 获取接口函数\n",
    "    def GetRecommendation(user):\n",
    "        seen_items = set(train[user]) if user in train else set()\n",
    "        gender = profile[user]['gender']\n",
    "        age = profile[user]['age']\n",
    "        country = profile[user]['country']\n",
    "        if gender not in items or age not in items[gender] or country not in items[gender][age]:\n",
    "            recs = mostPopular(user)\n",
    "        else:\n",
    "            recs = [x for x in items[gender][age][country] if x[0] not in seen_items][:N]\n",
    "        return recs\n",
    "    \n",
    "    return GetRecommendation"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 三. 实验\n",
    "\n",
    "采用Last.FM数据集，为加快实验速度，只取5000名用户的记录进行实验\n",
    "\n",
    "1. MostPopular实验\n",
    "2. GenderMostPopular实验\n",
    "3. AgeMostPopular实验\n",
    "4. CountryMostPopular实验\n",
    "5. DemographicMostPopular实验\n",
    "\n",
    "M=10, N=10"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "class Experiment():\n",
    "    \n",
    "    def __init__(self, M, N, at='MostPopular',\n",
    "                 fp='../dataset/lastfm-dataset-360K/usersha1-artmbid-artname-plays.tsv',\n",
    "                 up='../dataset/lastfm-dataset-360K/usersha1-profile.tsv'):\n",
    "        '''\n",
    "        :params: M, 进行多少次实验\n",
    "        :params: N, TopN推荐物品的个数\n",
    "        :params: fp, 数据文件路径\n",
    "        :params: up, 用户注册信息文件路径\n",
    "        '''\n",
    "        self.M = M\n",
    "        self.N = N\n",
    "        self.fp = fp\n",
    "        self.up = up\n",
    "        self.at = at\n",
    "        self.alg = {'MostPopular': MostPopular, 'GenderMostPopular': GenderMostPopular,\n",
    "                    'AgeMostPopular': AgeMostPopular, 'CountryMostPopular': CountryMostPopular,\n",
    "                    'DemographicMostPopular': DemographicMostPopular}\n",
    "    \n",
    "    # 定义单次实验\n",
    "    @timmer\n",
    "    def worker(self, train, test, profile):\n",
    "        '''\n",
    "        :params: train, 训练数据集\n",
    "        :params: test, 测试数据集\n",
    "        :params: profile, 用户注册信息\n",
    "        :return: 各指标的值\n",
    "        '''\n",
    "        getRecommendation = self.alg[self.at](train, profile, self.N)\n",
    "        metric = Metric(train, test, getRecommendation)\n",
    "        return metric.eval()\n",
    "    \n",
    "    # 多次实验取平均\n",
    "    @timmer\n",
    "    def run(self):\n",
    "        metrics = {'Precision': 0, 'Recall': 0, \n",
    "                   'Coverage': 0}\n",
    "        dataset = Dataset(self.fp, self.up)\n",
    "        for ii in range(self.M):\n",
    "            train, test, profile = dataset.splitData(self.M, ii)\n",
    "            print('Experiment {}:'.format(ii))\n",
    "            metric = self.worker(train, test, profile)\n",
    "            metrics = {k: metrics[k]+metric[k] for k in metrics}\n",
    "        metrics = {k: metrics[k] / self.M for k in metrics}\n",
    "        print('Average Result (M={}, N={}): {}'.format(\\\n",
    "                              self.M, self.N, metrics))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 1. MostPopular实验\n",
    "M, N = 10, 10\n",
    "exp = Experiment(M, N, at='MostPopular')\n",
    "exp.run()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 2. GenderMostPopular实验\n",
    "M, N = 10, 10\n",
    "exp = Experiment(M, N, at='GenderMostPopular')\n",
    "exp.run()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 3. AgeMostPopular实验\n",
    "M, N = 10, 10\n",
    "exp = Experiment(M, N, at='AgeMostPopular')\n",
    "exp.run()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "scrolled": true
   },
   "outputs": [],
   "source": [
    "# 4. CountryMostPopular实验\n",
    "M, N = 10, 10\n",
    "exp = Experiment(M, N, at='CountryMostPopular')\n",
    "exp.run()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 5. DemographicMostPopular实验\n",
    "M, N = 10, 10\n",
    "exp = Experiment(M, N, at='DemographicMostPopular')\n",
    "exp.run()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 四. 实验结果\n",
    "\n",
    "1. MostPopular实验\n",
    "\n",
    "    Running time: 340.1061339378357\n",
    "    \n",
    "    Average Result (M=10, N=10): {'Precision': 2.28, 'Recall': 4.635, 'Coverage': 0.071}\n",
    "\n",
    "2. GenderMostPopular实验\n",
    "\n",
    "    Running time: 265.6223392486572\n",
    "    \n",
    "    Average Result (M=10, N=10): {'Precision': 2.326, 'Recall': 4.727, 'Coverage': 0.107}\n",
    "\n",
    "3. AgeMostPopular实验\n",
    "\n",
    "    Running time: 205.56471586227417\n",
    "    \n",
    "    Average Result (M=10, N=10): {'Precision': 2.379, 'Recall': 4.822, 'Coverage': 0.455}\n",
    "    \n",
    "4. CountryMostPopular实验\n",
    "\n",
    "    Running time: 69.32589602470398\n",
    "    \n",
    "    Average Result (M=10, N=10): {'Precision': 2.415, 'Recall': 4.855, 'Coverage': 2.5}\n",
    "\n",
    "5. DemographicMostPopular实验\n",
    "\n",
    "    Running time: 73.98487401008606\n",
    "    \n",
    "    Average Result (M=10, N=10): {'Precision': 2.42, 'Recall': 4.86, 'Coverage': 2.5}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 附录：运行日志(请双击查看)\n",
    "\n",
    "1. MostPopular实验\n",
    "Func loadData, run time: 31.807884693145752\n",
    "Func splitData, run time: 0.6068239212036133\n",
    "Experiment 0:\n",
    "Metric: {'Precision': 2.27, 'Recall': 4.61, 'Coverage': 0.08}\n",
    "Func worker, run time: 28.971026182174683\n",
    "Func splitData, run time: 0.44267868995666504\n",
    "Experiment 1:\n",
    "Metric: {'Precision': 2.26, 'Recall': 4.59, 'Coverage': 0.07}\n",
    "Func worker, run time: 28.366169691085815\n",
    "Func splitData, run time: 0.5044941902160645\n",
    "Experiment 2:\n",
    "Metric: {'Precision': 2.22, 'Recall': 4.5, 'Coverage': 0.07}\n",
    "Func worker, run time: 38.45435905456543\n",
    "Func splitData, run time: 0.4402909278869629\n",
    "Experiment 3:\n",
    "Metric: {'Precision': 2.31, 'Recall': 4.67, 'Coverage': 0.07}\n",
    "Func worker, run time: 28.688590049743652\n",
    "Func splitData, run time: 0.48880600929260254\n",
    "Experiment 4:\n",
    "Metric: {'Precision': 2.33, 'Recall': 4.77, 'Coverage': 0.07}\n",
    "Func worker, run time: 28.041622161865234\n",
    "Func splitData, run time: 0.5104248523712158\n",
    "Experiment 5:\n",
    "Metric: {'Precision': 2.2, 'Recall': 4.51, 'Coverage': 0.07}\n",
    "Func worker, run time: 28.33553171157837\n",
    "Func splitData, run time: 0.5063672065734863\n",
    "Experiment 6:\n",
    "Metric: {'Precision': 2.33, 'Recall': 4.72, 'Coverage': 0.07}\n",
    "Func worker, run time: 29.89447808265686\n",
    "Func splitData, run time: 0.4293487071990967\n",
    "Experiment 7:\n",
    "Metric: {'Precision': 2.24, 'Recall': 4.54, 'Coverage': 0.07}\n",
    "Func worker, run time: 29.959038019180298\n",
    "Func splitData, run time: 0.5041751861572266\n",
    "Experiment 8:\n",
    "Metric: {'Precision': 2.34, 'Recall': 4.71, 'Coverage': 0.07}\n",
    "Func worker, run time: 30.90305995941162\n",
    "Func splitData, run time: 0.4769570827484131\n",
    "Experiment 9:\n",
    "Metric: {'Precision': 2.3, 'Recall': 4.73, 'Coverage': 0.07}\n",
    "Func worker, run time: 31.461721897125244\n",
    "Average Result (M=10, N=10): {'Precision': 2.2800000000000002, 'Recall': 4.635, 'Coverage': 0.07100000000000002}\n",
    "Func run, run time: 340.1061339378357\n",
    "\n",
    "2. GenderMostPopular实验\n",
    "Func loadData, run time: 32.967588901519775\n",
    "Func splitData, run time: 0.45601916313171387\n",
    "Experiment 0:\n",
    "Metric: {'Precision': 2.32, 'Recall': 4.72, 'Coverage': 0.11}\n",
    "Func worker, run time: 19.60267210006714\n",
    "Func splitData, run time: 0.5031688213348389\n",
    "Experiment 1:\n",
    "Metric: {'Precision': 2.33, 'Recall': 4.73, 'Coverage': 0.1}\n",
    "Func worker, run time: 25.32190990447998\n",
    "Func splitData, run time: 0.42341017723083496\n",
    "Experiment 2:\n",
    "Metric: {'Precision': 2.38, 'Recall': 4.82, 'Coverage': 0.1}\n",
    "Func worker, run time: 21.993903875350952\n",
    "Func splitData, run time: 0.505748987197876\n",
    "Experiment 3:\n",
    "Metric: {'Precision': 2.4, 'Recall': 4.86, 'Coverage': 0.1}\n",
    "Func worker, run time: 25.69679093360901\n",
    "Func splitData, run time: 0.5266520977020264\n",
    "Experiment 4:\n",
    "Metric: {'Precision': 2.22, 'Recall': 4.54, 'Coverage': 0.11}\n",
    "Func worker, run time: 21.68884301185608\n",
    "Func splitData, run time: 0.5205812454223633\n",
    "Experiment 5:\n",
    "Metric: {'Precision': 2.31, 'Recall': 4.74, 'Coverage': 0.11}\n",
    "Func worker, run time: 24.315038204193115\n",
    "Func splitData, run time: 0.6323270797729492\n",
    "Experiment 6:\n",
    "Metric: {'Precision': 2.33, 'Recall': 4.7, 'Coverage': 0.11}\n",
    "Func worker, run time: 24.59072780609131\n",
    "Func splitData, run time: 0.5137548446655273\n",
    "Experiment 7:\n",
    "Metric: {'Precision': 2.35, 'Recall': 4.76, 'Coverage': 0.11}\n",
    "Func worker, run time: 21.767657995224\n",
    "Func splitData, run time: 0.5112850666046143\n",
    "Experiment 8:\n",
    "Metric: {'Precision': 2.3, 'Recall': 4.64, 'Coverage': 0.11}\n",
    "Func worker, run time: 21.0389301776886\n",
    "Func splitData, run time: 0.5135531425476074\n",
    "Experiment 9:\n",
    "Metric: {'Precision': 2.32, 'Recall': 4.76, 'Coverage': 0.11}\n",
    "Func worker, run time: 21.189006090164185\n",
    "Average Result (M=10, N=10): {'Precision': 2.326, 'Recall': 4.726999999999999, 'Coverage': 0.10700000000000001}\n",
    "Func run, run time: 265.6223392486572\n",
    "\n",
    "3. AgeMostPopular实验\n",
    "Func loadData, run time: 30.702067852020264\n",
    "Func splitData, run time: 3.189789295196533\n",
    "Experiment 0:\n",
    "Metric: {'Precision': 2.34, 'Recall': 4.75, 'Coverage': 0.45}\n",
    "Func worker, run time: 14.813374996185303\n",
    "Func splitData, run time: 0.5000729560852051\n",
    "Experiment 1:\n",
    "Metric: {'Precision': 2.36, 'Recall': 4.78, 'Coverage': 0.46}\n",
    "Func worker, run time: 18.69653296470642\n",
    "Func splitData, run time: 0.714263916015625\n",
    "Experiment 2:\n",
    "Metric: {'Precision': 2.5, 'Recall': 5.06, 'Coverage': 0.46}\n",
    "Func worker, run time: 16.40371298789978\n",
    "Func splitData, run time: 0.42107224464416504\n",
    "Experiment 3:\n",
    "Metric: {'Precision': 2.41, 'Recall': 4.86, 'Coverage': 0.47}\n",
    "Func worker, run time: 16.157140016555786\n",
    "Func splitData, run time: 0.44391894340515137\n",
    "Experiment 4:\n",
    "Metric: {'Precision': 2.32, 'Recall': 4.73, 'Coverage': 0.45}\n",
    "Func worker, run time: 16.057992935180664\n",
    "Func splitData, run time: 0.4891808032989502\n",
    "Experiment 5:\n",
    "Metric: {'Precision': 2.33, 'Recall': 4.75, 'Coverage': 0.45}\n",
    "Func worker, run time: 16.826287746429443\n",
    "Func splitData, run time: 0.5030360221862793\n",
    "Experiment 6:\n",
    "Metric: {'Precision': 2.38, 'Recall': 4.8, 'Coverage': 0.46}\n",
    "Func worker, run time: 15.631379127502441\n",
    "Func splitData, run time: 0.508782148361206\n",
    "Experiment 7:\n",
    "Metric: {'Precision': 2.32, 'Recall': 4.7, 'Coverage': 0.45}\n",
    "Func worker, run time: 15.04989218711853\n",
    "Func splitData, run time: 0.4729750156402588\n",
    "Experiment 8:\n",
    "Metric: {'Precision': 2.5, 'Recall': 5.02, 'Coverage': 0.46}\n",
    "Func worker, run time: 19.40653085708618\n",
    "Func splitData, run time: 0.4343280792236328\n",
    "Experiment 9:\n",
    "Metric: {'Precision': 2.33, 'Recall': 4.77, 'Coverage': 0.44}\n",
    "Func worker, run time: 17.756699085235596\n",
    "Average Result (M=10, N=10): {'Precision': 2.379, 'Recall': 4.822, 'Coverage': 0.45500000000000007}\n",
    "Func run, run time: 205.56471586227417\n",
    "\n",
    "4. CountryMostPopular实验\n",
    "Func loadData, run time: 30.24172306060791\n",
    "Func splitData, run time: 0.4623088836669922\n",
    "Experiment 0:\n",
    "Metric: {'Precision': 2.41, 'Recall': 4.85, 'Coverage': 2.53}\n",
    "Func worker, run time: 3.4046268463134766\n",
    "Func splitData, run time: 0.43274497985839844\n",
    "Experiment 1:\n",
    "Metric: {'Precision': 2.39, 'Recall': 4.79, 'Coverage': 2.49}\n",
    "Func worker, run time: 3.3446362018585205\n",
    "Func splitData, run time: 0.5451531410217285\n",
    "Experiment 2:\n",
    "Metric: {'Precision': 2.35, 'Recall': 4.72, 'Coverage': 2.52}\n",
    "Func worker, run time: 3.223905086517334\n",
    "Func splitData, run time: 0.4849269390106201\n",
    "Experiment 3:\n",
    "Metric: {'Precision': 2.39, 'Recall': 4.76, 'Coverage': 2.5}\n",
    "Func worker, run time: 3.1288833618164062\n",
    "Func splitData, run time: 0.44620418548583984\n",
    "Experiment 4:\n",
    "Metric: {'Precision': 2.4, 'Recall': 4.86, 'Coverage': 2.48}\n",
    "Func worker, run time: 3.3267481327056885\n",
    "Func splitData, run time: 0.4455292224884033\n",
    "Experiment 5:\n",
    "Metric: {'Precision': 2.4, 'Recall': 4.86, 'Coverage': 2.53}\n",
    "Func worker, run time: 3.6315438747406006\n",
    "Func splitData, run time: 0.5009069442749023\n",
    "Experiment 6:\n",
    "Metric: {'Precision': 2.38, 'Recall': 4.76, 'Coverage': 2.5}\n",
    "Func worker, run time: 4.013977766036987\n",
    "Func splitData, run time: 0.5759139060974121\n",
    "Experiment 7:\n",
    "Metric: {'Precision': 2.47, 'Recall': 4.96, 'Coverage': 2.48}\n",
    "Func worker, run time: 3.3212859630584717\n",
    "Func splitData, run time: 0.4942488670349121\n",
    "Experiment 8:\n",
    "Metric: {'Precision': 2.53, 'Recall': 5.05, 'Coverage': 2.48}\n",
    "Func worker, run time: 3.3335418701171875\n",
    "Func splitData, run time: 0.43490123748779297\n",
    "Experiment 9:\n",
    "Metric: {'Precision': 2.43, 'Recall': 4.94, 'Coverage': 2.49}\n",
    "Func worker, run time: 3.2024052143096924\n",
    "Average Result (M=10, N=10): {'Precision': 2.415, 'Recall': 4.854999999999999, 'Coverage': 2.5}\n",
    "Func run, run time: 69.32589602470398\n",
    "\n",
    "5. DemographicMostPopular实验\n",
    "Func loadData, run time: 31.048221826553345\n",
    "Func splitData, run time: 0.6405370235443115\n",
    "Experiment 0:\n",
    "Metric: {'Precision': 2.41, 'Recall': 4.85, 'Coverage': 2.53}\n",
    "Func worker, run time: 3.8784639835357666\n",
    "Func splitData, run time: 0.5229880809783936\n",
    "Experiment 1:\n",
    "Metric: {'Precision': 2.39, 'Recall': 4.79, 'Coverage': 2.49}\n",
    "Func worker, run time: 3.4125118255615234\n",
    "Func splitData, run time: 0.43076014518737793\n",
    "Experiment 2:\n",
    "Metric: {'Precision': 2.35, 'Recall': 4.72, 'Coverage': 2.52}\n",
    "Func worker, run time: 3.470226287841797\n",
    "Func splitData, run time: 0.4595067501068115\n",
    "Experiment 3:\n",
    "Metric: {'Precision': 2.39, 'Recall': 4.76, 'Coverage': 2.5}\n",
    "Func worker, run time: 3.9592530727386475\n",
    "Func splitData, run time: 0.5331680774688721\n",
    "Experiment 4:\n",
    "Metric: {'Precision': 2.4, 'Recall': 4.86, 'Coverage': 2.48}\n",
    "Func worker, run time: 4.06541109085083\n",
    "Func splitData, run time: 0.5062789916992188\n",
    "Experiment 5:\n",
    "Metric: {'Precision': 2.4, 'Recall': 4.86, 'Coverage': 2.53}\n",
    "Func worker, run time: 3.6064438819885254\n",
    "Func splitData, run time: 0.4431419372558594\n",
    "Experiment 6:\n",
    "Metric: {'Precision': 2.38, 'Recall': 4.76, 'Coverage': 2.5}\n",
    "Func worker, run time: 3.7960410118103027\n",
    "Func splitData, run time: 0.5151360034942627\n",
    "Experiment 7:\n",
    "Metric: {'Precision': 2.47, 'Recall': 4.96, 'Coverage': 2.48}\n",
    "Func worker, run time: 4.031769037246704\n",
    "Func splitData, run time: 0.4504380226135254\n",
    "Experiment 8:\n",
    "Metric: {'Precision': 2.53, 'Recall': 5.05, 'Coverage': 2.48}\n",
    "Func worker, run time: 3.66860294342041\n",
    "Func splitData, run time: 0.5212371349334717\n",
    "Experiment 9:\n",
    "Metric: {'Precision': 2.43, 'Recall': 4.94, 'Coverage': 2.49}\n",
    "Func worker, run time: 3.6845691204071045\n",
    "Average Result (M=10, N=10): {'Precision': 2.415, 'Recall': 4.854999999999999, 'Coverage': 2.5}\n",
    "Func run, run time: 73.98487401008606"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.5"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
